export class ImageResize {
	constructor(quill, options = {}) {
		this.quill = quill
		this.options = options
		this.handleClick = this.handleClick.bind(this)
		this.handleMousedown = this.handleMousedown.bind(this)
		this.handleMouseup = this.handleMouseup.bind(this)
		this.handleDrag = this.handleDrag.bind(this)
		this.checkImage = this.checkImage.bind(this)
		this.boxes = []
		document.execCommand('enableObjectResizing', false, 'false')
		this.quill.root.addEventListener('click', this.handleClick, false)
	}
	handleClick(evt) {
		if (evt.target && evt.target.tagName && evt.target.tagName.toUpperCase() == 'IMG') {
			if (this.img === evt.target) {
				return
			}
			if (this.img) {
				this.hide()
			}
			this.show(evt.target)
		}
		else if (this.img) {
			this.hide()
		}
	}
	show(img) {
		this.img = img
		this.showResizers()
		this.showSizeDisplay()
		const rect = this.img.getBoundingClientRect()
		this.positionBoxes(rect)
		this.positionSizeDisplay(rect)
	}
	hide() {
		this.hideResizers()
		this.hideSizeDisplay()
		this.img = undefined
	}
	showResizers() {
		this.setUserSelect('none')
		this.addBox('nwse-resize')
		this.addBox('nesw-resize')
		this.addBox('nwse-resize')
		this.addBox('nesw-resize')
		document.addEventListener('keyup', this.checkImage, true)
		this.quill.root.addEventListener('input', this.checkImage, true)
	}
	hideResizers() {
		// stop listening for image deletion or movement
		document.removeEventListener('keyup', this.checkImage)
		this.quill.root.removeEventListener('input', this.checkImage)
		// reset user-select
		this.setUserSelect('')
		this.setCursor('')
		// remove boxes
		this.boxes.forEach(box => document.body.removeChild(box))
		// release memory
		this.dragBox = undefined
		this.dragStartX = undefined
		this.preDragWidth = undefined
		this.boxes = []
	}

	addBox(cursor) {
		// create div element for resize handle
		const box = document.createElement('div')
		// apply styles
		const styles = {
			position: 'absolute',
			height: '12px',
			width: '12px',
			backgroundColor: 'white',
			border: '1px solid #777',
			boxSizing: 'border-box',
			opacity: '0.80',
			zIndex: 9,
			cursor: cursor,
		}
		this.extend(box.style, styles, this.options.handleStyles || {})
		// listen for mousedown on each box
		box.addEventListener('mousedown', this.handleMousedown, false)
		// add drag handle to document
		document.body.appendChild(box)
		// keep track of drag handle
		this.boxes.push(box)
	}

	extend(destination, ...sources) {
		sources.forEach(source => {
			for (let prop in source) {
				if (source.hasOwnProperty(prop)) {
					destination[prop] = source[prop]
				}
			}
		})
		return destination
	}

	positionBoxes(rect) {
		// set the top and left for each drag handle
		[
			{ left: rect.left - 6,              top: rect.top - 6 },               // top left
			{ left: rect.left + rect.width - 6, top: rect.top - 6 },               // top right
			{ left: rect.left + rect.width - 6, top: rect.top + rect.height - 6 }, // bottom right
			{ left: rect.left - 6,              top: rect.top + rect.height - 6 }, // bottom left
		].forEach((pos, idx) => {
			this.extend(this.boxes[idx].style, {
				top: Math.round(pos.top + window.pageYOffset) + 'px',
				left: Math.round(pos.left + window.pageXOffset) + 'px',
			})
		})
	}

	handleMousedown(evt) {
		// note which box
		this.dragBox = evt.target
		// note starting mousedown position
		this.dragStartX = evt.clientX
		// store the width before the drag
		this.preDragWidth = this.img.width || this.img.naturalWidth
		// set the proper cursor everywhere
		this.setCursor(this.dragBox.style.cursor)
		// listen for movement and mouseup
		document.addEventListener('mousemove', this.handleDrag, false)
		document.addEventListener('mouseup', this.handleMouseup, false)
	}

	handleMouseup() {
		// reset cursor everywhere
		this.setCursor('')
		// stop listening for movement and mouseup
		document.removeEventListener('mousemove', this.handleDrag)
		document.removeEventListener('mouseup', this.handleMouseup)
	}

	handleDrag(evt) {
		if (!this.img) {
			// image not set yet
			return
		}
		// update image size
		if (this.dragBox == this.boxes[0] || this.dragBox == this.boxes[3]) {
			// left-side resize handler draging right shrinks image
			this.img.width = Math.round(this.preDragWidth - evt.clientX - this.dragStartX)
		}
		else {
			// right-side resize handler draging right enlarges image
			this.img.width = Math.round(this.preDragWidth + evt.clientX - this.dragStartX)
		}
		// reposition the drag handles around the image
		const rect = this.img.getBoundingClientRect()
		this.positionBoxes(rect)
		this.positionSizeDisplay(rect)
	}

	setUserSelect(value) {
		[
			'userSelect',
			'mozUserSelect',
			'webkitUserSelect',
			'msUserSelect'
		].forEach(prop => {
			// set on contenteditable element and <html>
			this.quill.root.style[prop] = value
			document.documentElement.style[prop] = value
		})
	}

	setCursor(value) {
		[
			document.body,
			this.img,
			this.quill.root
		].forEach(el => el.style.cursor = value)
	}

	checkImage() {
		if (this.img) {
			this.hide()
		}
	}

	showSizeDisplay() {
		if (!this.options.displaySize) {
			return
		}
		this.display = document.createElement('div')
		// apply styles
		const styles = {
			position: 'absolute',
			font: '12px/1.0 Arial, Helvetica, sans-serif',
			padding: '4px 8px',
			textAlign: 'center',
			backgroundColor: 'white',
			color: '#333',
			border: '1px solid #777',
			boxSizing: 'border-box',
			opacity: '0.80',
			cursor: 'default',
		}
		this.extend(this.display.style, styles, this.options.displayStyles || {})
		document.body.appendChild(this.display)
	}

	hideSizeDisplay() {
		document.body.removeChild(this.display)
		this.display = undefined
	}

	positionSizeDisplay(rect) {
		if (!this.display || !this.img) {
			return
		}
		const size = this.getCurrentSize()
		this.display.innerHTML = size.join(' &times ')
		if (size[0] > 120 && size[1] > 30) {
			// position on top of image
			const dispRect = this.display.getBoundingClientRect()
			this.extend(this.display.style, {
				left: Math.round(rect.left + rect.width + window.pageXOffset - dispRect.width - 8) + 'px',
				top: Math.round(rect.top + rect.height + window.pageYOffset - dispRect.height - 8) + 'px',
			})
		}
		else {
			// position off bottom right
			this.extend(this.display.style, {
				left: Math.round(rect.left + rect.width + window.pageXOffset + 8) + 'px',
				top: Math.round(rect.top + rect.height + window.pageYOffset + 8) + 'px',
			})
		}
	}

	getCurrentSize() {
		return [
			this.img.width,
			Math.round(this.img.width / this.img.naturalWidth * this.img.naturalHeight),
		]
	}

}
